package com.mmall.demo2.common.serialize;

import com.mmall.demo2.utils.DateTimeUtil;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.text.SimpleDateFormat;

/**
 * @Author: jiaru
 * @Description: json字符串及对象操作
 * @Date: Created on 2018/6/28
 * @Modify:
 */
public class JsonMapper {
    // 日志
    private static Logger log = LoggerFactory.getLogger(JsonMapper.class);

    // 用于 Java 对象的序列化及反序列化
    private static ObjectMapper objectMapper = new ObjectMapper();

    // objectMapper 配置
    static {
        // 对象的所有字段全部列入
        // Inclusion.ALWAYS         所有字段都参加序列化及反序列化
        // Inclusion.NON_NULL       忽略空值，只有非空的字段，才参加序列化及反序列化操作
        // Inclusion.NON_DEFAULT    忽略空值、默认值，只有非空的字段，才参加序列化操作
        // Inclusion.NON_DEFAULT    忽略空值、默认值、长度是0的字符串、size是0的集合，只有非空的字段，才参加序列化操作
        // 规则的级别排序为：
        // ALWAYS < NON_NULL < NON_DEFAULT < NON_DEFAULT
        objectMapper.setSerializationInclusion(Inclusion.ALWAYS);

        // 忽略空bean转json的错误
        // true     : 当json、java对象没有任何属性，会抛出空 bean 异常
        // false    : 忽略空bean异常，直接返回：{}，或者空的java对象
        objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);

        // 忽略json字符串存在，但在Java对象中不存在对应属性的情况，防止错误
        objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 是否将时间转换成时间戳
        // true     : 1534264470185
        // false    : 2018-08-14T16:34:54.994+0000
        // 取消默认转换timestamps
        objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);

        // 所有日期格式都统一为：yyyy-MM-dd HH:mm:ss
        objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));

    }

    /**
     * 对象转换json字符串
     * @param obj   任意对象
     * @param <T>   将此方法声明为泛型方法，可传入任何对象
     * @return      json字符串
     */
    public static <T> String obj2String(T obj) {
        if (obj == null) {
            return null;
        }
        // 如果是String，则返回原对象，否则执行对象转json
        try {
            return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("Parse Object to String error:", e);
            return null;
        }
    }

    /**
     * 对象转换json字符串，并返回格式化好的json字符串
     * @param obj   任意对象
     * @param <T>   将此方法声明为泛型方法，可传入任何对象
     * @return      json字符串
     */
    public static <T> String obj2StringPretty(T obj) {
        if (obj == null) {
            return null;
        }
        // 如果是String，则返回原对象，否则执行对象转json
        try {
            return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("Parse Object to String error:", e);
            return null;
        }
    }

    /**
     * json字符串转换对象
     * @param str   json字符串
     * @param clazz 转换目标对象
     * @param <T>   将此方法声明为泛型方法，可传入任何对象
     * @return <T>  目标对象
     */
    public static <T> T string2Obj(String str, Class<T> clazz) {
        if (StringUtils.isEmpty(str) || clazz == null) {
            return null;
        }

        try {
            return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
        } catch (IOException e) {
            log.warn("Parse String to Object error:", e);
            return null;
        }
    }

    /**
     * json字符串转换对象
     * @param str           json字符串
     * @param typeReference 转换目标对象
     * @param <T>           将此方法声明为泛型方法，可传入任何对象
     * @return <T>          目标对象的集合
     */
    public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
        if (StringUtils.isEmpty(str) || typeReference == null) {
            return null;
        }

        try {
            return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
            // return typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference);
        } catch (IOException e) {
            log.warn("Parse String to Object error:", e);
            return null;
        }
    }

    /**
     * json 字符串转换成“集合对象”
     * @param str               json字符串
     * @param collectionClass   集合类型
     * @param elementClass      Java对象
     * @param <T>               将此方法声明为泛型方法，可传入任何对象
     * @return                  目标对象的集合
     */
    public static <T> T string2Obj(String str, Class<?> collectionClass, Class<?>... elementClass) {
        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClass);


        try {
            return objectMapper.readValue(str, javaType);
        } catch (IOException e) {
            log.warn("Parse String to Object error:", e);
            return null;
        }
    }


    public static void main(String[] args) {
        // User u1 = new User();
        // u1.setId(1);
        // u1.setCreateTime(new Date());
        // u1.setEmail("jiarupc@qq.com");
        // String user1JsonPretty = JsonMapper.obj2StringPretty(u1);
        // System.out.println(user1JsonPretty);
        // User u2 = new User();
        // u2.setId(2);
        // u2.setEmail("jiarupc@163.com");

        // String user1Json = JsonMapper.obj2String(u1);
        // String user1JsonPretty = JsonMapper.obj2StringPretty(u1);


        // log.info(user1Json);
        // log.info(user1JsonPretty);

        // User user = JsonMapper.string2Obj(user1Json, User.class);

        // List<User> userList = Lists.newArrayList();
        // userList.add(u1);
        // userList.add(u2);

        // String userListStr = JsonMapper.obj2StringPretty(userList);
        // log.info("=========================");
        // log.info(userListStr);

        // List<User> userListObj1 = JsonMapper.string2Obj(userListStr, new TypeReference<List<User>>() {
        // });

        // List<User> userListObj2 = JsonMapper.string2Obj(userListStr, List.class, User.class);

        // System.out.println("end");
    }
}
